#include "Acceptor.h"
#include <fcntl.h>
#include <memory>
#include <unistd.h>
#include "AcceptChannel.h"
#include "Debug.h"
#include "EventLoop.h"
#include "InnerType.h"
#include "IpV4Address.h"
#include "IpV6Address.h"
#include "PlatLogger.h"
#include "Socket.h"
#include "SocketOpt.h"
#include "TcpSocket.h"
#include "UnixDomainAddress.h"
#include "UnixDomainSocket.h"
#include "Util.h"
#include "XnccAddress.h"

namespace xncc {
namespace foundation {
    Acceptor::Acceptor(EventLoop* loop, std::shared_ptr<XnccAddress> listenAddr, bool reuseport)
        : loop_(loop), listenning_(false), idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
    {
        debug_assert(idleFd_ >= 0, "open /dev/null error");
        if (listenAddr->Type() == shared::XNCC_ADDR_TYPE::IPV4_TCP ||
            listenAddr->Type() == shared::XNCC_ADDR_TYPE::IPV6_TCP) {
            acceptSocket_ = shared::createUniquePtr<TcpSocket>(listenAddr);
            SocketOpt::setReuseAddr(true, acceptSocket_->fd());
            SocketOpt::setReusePort(reuseport, acceptSocket_->fd());
        }
        else if (listenAddr->Type() == shared::XNCC_ADDR_TYPE::UNIX_DOMAIN) {
            acceptSocket_ = shared::createUniquePtr<UnixDomainSocket>(listenAddr);
        }
        acceptChannel_ = std::make_shared<AcceptChannel>(loop, acceptSocket_->fd());
        acceptSocket_->bind();
    }

    void Acceptor::init(const std::set<std::string>& blacklist)
    {
        blacklist_ = blacklist;
        acceptChannel_->init();
        acceptChannel_->setReadCallBack([this]() { handleRead(); });
    }

    Acceptor::~Acceptor()
    {
        acceptChannel_->disableAll();
        acceptChannel_->remove();
        ::close(idleFd_);
    }

    void Acceptor::listen()
    {
        listenning_ = true;
        acceptSocket_->listen();
        acceptChannel_->enableReading();
    }

    void Acceptor::handleRead()
    {
        std::shared_ptr<XnccAddress> peerAddr;
        if (acceptSocket_->addrType() == shared::XNCC_ADDR_TYPE::IPV4_TCP) {
            peerAddr = std::make_shared<IpV4Address>();
        }
        else if (acceptSocket_->addrType() == shared::XNCC_ADDR_TYPE::IPV6_TCP) {
            peerAddr = std::make_shared<IpV6Address>();
        }
        else if (acceptSocket_->addrType() == shared::XNCC_ADDR_TYPE::UNIX_DOMAIN) {
            peerAddr = std::make_shared<UnixDomainAddress>();
        }
        // FIXME loop until no more
        int connfd = acceptSocket_->accept(peerAddr);
        if (connfd >= 0) {
            const auto addrStr = peerAddr->toStr();
            const auto ipStr   = addrStr.substr(0, addrStr.find_last_of(':'));
            if (blacklist_.find(ipStr) != blacklist_.end()) {
                SocketOpt::close(connfd);
                TRACE_ERROR << ("addr:"_s) << addrStr << " in blacklist engine:"_s << loop_->id();
            }
            else {
                if (newConnectionCallback_) {
                    newConnectionCallback_(connfd, peerAddr);
                    /// 新的连接到达之后，直接将fd分配到acceptor的线程去处理
                }
                else {
                    SocketOpt::close(connfd);
                    TRACE_ERROR << ("engine:"_s) << loop_->id() << " has no register newConnectionCallback"_s;
                }
            }
        }
        else {
            SYSTEM_ERROR << ("accept an error fd:"_s) << connfd;
            if (errno == EMFILE) {
                ::close(idleFd_);
                idleFd_ = ::accept(acceptSocket_->fd(), nullptr, nullptr);
                ::close(idleFd_);
                idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
            }
        }
    }
}  // namespace foundation
}  // namespace xncc
